"""visitor.py."""
from __future__ import print_function

import sys
from asdl import ast


class AsdlVisitor:
    """Base class for visitors.

    TODO:
    - It might be useful to separate this into VisitChildren() / generic_visit()
      like Python's ast.NodeVisitor  does.
    - Also remove self.f and self.Emit.  Those can go in self.output?
    - Move to common location, since gen_python uses it as well.
    """

    def __init__(self, f):
        self.f = f
        self.current_depth = 0  # the current number of indent levels

    def Indent(self):
        self.current_depth += 1

    def Dedent(self):
        self.current_depth -= 1

    def Emit(self, s, depth=-1, reflow=True):
        if depth == -1:
            depth = self.current_depth
        for line in FormatLines(s, depth, reflow=reflow):
            self.f.write(line)

    def VisitModule(self, mod):
        for dfn in mod.dfns:
            if isinstance(dfn, ast.SubTypeDecl):
                self.VisitSubType(dfn)
            else:
                self.VisitType(dfn)
        self.EmitFooter()

    def VisitSubType(self, subtype):
        pass

    def VisitType(self, typ, depth=0):
        if isinstance(typ.value, ast.SimpleSum):
            self.VisitSimpleSum(typ.value, typ.name, depth)

        elif isinstance(typ.value, ast.Sum):
            self.VisitCompoundSum(typ.value, typ.name, depth)

        elif isinstance(typ.value, ast.Product):
            self.VisitProduct(typ.value, typ.name, depth)

        else:
            raise AssertionError(typ)

    # Optionally overridden.
    def VisitSimpleSum(self, value, name, depth):
        pass

    def VisitCompoundSum(self, value, name, depth):
        pass

    def VisitProduct(self, value, name, depth):
        pass

    def EmitFooter(self):
        pass


TABSIZE = 2
MAX_COL = 80

# Copied from asdl_c.py


def _ReflowLines(s, depth):
    """Reflow the line s indented depth tabs.

    Return a sequence of lines where no line extends beyond MAX_COL when
    properly indented.  The first line is properly indented based
    exclusively on depth * TABSIZE.  All following lines -- these are
    the reflowed lines generated by this function -- start at the same
    column as the first character beyond the opening { in the first
    line.
    """
    size = MAX_COL - depth * TABSIZE
    if len(s) < size:
        return [s]

    lines = []
    cur = s
    padding = ""
    while len(cur) > size:
        i = cur.rfind(' ', 0, size)
        if i == -1:
            if 0:
                print(
                    "Warning: No space to reflow line (size=%d, depth=%d, cur=%r): %r"
                    % (size, depth, cur, s),
                    file=sys.stderr)
            lines.append(padding + cur)
            break

        lines.append(padding + cur[:i])
        if len(lines) == 1:
            # find new size based on brace
            j = cur.find('{', 0, i)
            if j >= 0:
                j += 2  # account for the brace and the space after it
                size -= j
                padding = " " * j
            else:
                j = cur.find('(', 0, i)
                if j >= 0:
                    j += 1  # account for the paren (no space after it)
                    size -= j
                    padding = " " * j
        cur = cur[i + 1:]
    else:
        lines.append(padding + cur)
    return lines


def FormatLines(s, depth, reflow=True):
    """Make the generated code readable.

    Args:
      depth: controls indentation
      reflow: line wrapping.
    """
    if reflow:
        lines = _ReflowLines(s, depth)
    else:
        lines = [s]

    result = []
    for line in lines:
        line = (" " * TABSIZE * depth) + line + "\n"
        result.append(line)
    return result


"""
For */*_gen.py, and asdl/gen_*.py

from asdl.util import Write

def f():
    # constant string with leading stuff stripped off
    Write(f, '''
        hello
        there
       |''')

    # - option to reflow
    # - option to add MORE depth, in addition to stripping whitespace
    #   - I wonder if YSH template strings could use something like that
    Write(f, '''
        a = {a}
        b = {b}
       |''', locals(), reflow=True, depth=2)

    with Printer(locals()) as p:
      p.Write('''
          a = {a}
          b = {b}
         |''')
"""



